/**
 * Copyright 2011 Renato de Jesus Manzoni
 * 
 * SED - Software Engineering and Dependability Research Group
 * 
 *     http://www.sed.ic.unicamp.br/
 * 
 * Distributed Systems Laboratory (LSD) in the Institute of Computing (IC) at University of Campinas (UNICAMP)
 * 
 *     http://www.lsd.ic.unicamp.br/
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package br.unicamp.ic.lsd.wscaa;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.endpoint.ClientCallback;
import org.perf4j.StopWatch;
import org.perf4j.commonslog.CommonsLogStopWatch;

import br.unicamp.ic.lsd.wscaa.api.ServiceRequest;
import br.unicamp.ic.lsd.wscaa.api.ServiceResponse;
import br.unicamp.ic.lsd.wscaa.concurrent.WSCAACallBackResults;

//TODO fazer graficos usandos gnu-plot e http://perf4j.codehaus.org/apidocs/org/perf4j/log4j/package-summary.html

/**
 * @author Renato de Jesus Manzoni <rjmanzoni@gmail.com>
 * 
 */
public class WSCAAExecutor {
    private final List<WebService> wss;
    private final WSCAManager manager;
    // private static final long TIME_WINDOW_RESOLUTION = 20L;

    // Map<String, BindingOperationInfo> bindOps;
    // Map<BindingOperationInfo, WebService> opWs;

    private Map<String, WebService> opWs;

    private static Log logger = LogFactory.getLog(WSCAAExecutor.class);

    // public WSCAAExecutor(List<WebService> wss) {
    // this.wss = wss;
    // buildWsOpMap();
    // }

    /**
     * @param wss2
     * @param manager
     */
    public WSCAAExecutor(List<WebService> wss, WSCAManager manager) {
	this.wss = wss;
	this.manager = manager;
	buildWsOpMap();
    }

    private void buildWsOpMap() {

	opWs = new HashMap<String, WebService>();

	for (Iterator<WebService> iterator = wss.iterator(); iterator.hasNext();) {

	    WebService ws = iterator.next();

	    String[] ops = ws.getOperations();

	    for (int i = 0; i < ops.length; i++) {
		opWs.put(ops[i], ws);
	    }
	}

    }

    // talvez um LinkedHashMap com operations e objetos do parametro seja
    // melhor, porem a seguencia de opereations nao importa!!!
    public void executeWSCAA(Map<String, Object[]> workParallelFlow) {

	Map<String, ClientCallback> callBacksByOp = new HashMap<String, ClientCallback>();

	try {
	    for (Iterator<String> iterator = workParallelFlow.keySet()
		    .iterator(); iterator.hasNext();) {

		String key = iterator.next(); // get the key

		// BindingOperationInfo bo = bindOps.get(key); //get the
		// operation by key
		final StopWatch stopWatch = new CommonsLogStopWatch();

		WebService ws = opWs.get(key); // get the WebService that
					       // contain the key
		stopWatch.stop("WSCAAExecutor.executeWSCAA.opWs.get");

		ClientCallback cb = new ClientCallback();

		ws.invokeAsyncMethod(cb, key, workParallelFlow.get(key)); // invoke
									  // the
									  // service

		callBacksByOp.put(key, cb);

	    }

	} catch (Exception e) {
	    e.printStackTrace();
	}
    }

    public List<ServiceResponse> executeWSCAA(
	    List<ServiceRequest> workParallelFlow, long timeoutExceptionResolution) {

	final StopWatch stopWatch = new CommonsLogStopWatch();

	logger.debug("Invoking webservices asynchronously");

	WSCAACallBackResults callBacks = new WSCAACallBackResults(manager);

	manager.start(timeoutExceptionResolution);

	try {
	    for (Iterator<ServiceRequest> iterator = workParallelFlow
		    .iterator(); iterator.hasNext();) {

		ServiceRequest request = iterator.next(); // get the key

		// get the WebService that contain the key
		WebService ws = opWs.get(request.getRequestName());

		ClientCallback cb = new ClientCallback();

		// invoke the service
		ws.invokeAsyncMethod(cb, request.getRequestName(),
			request.getParams());

		callBacks.addTask(request.getRequestName(), request.getTimeout(), cb);

	    }

	} catch (Exception e) {
	    logger.error("Error invoke sync web services", e);
	}

	// stopWatch.stop("WSCAAExecutor.executeWSCAA.execution");
	//
	// final StopWatch stopWatchReturn = new CommonsLogStopWatch();

	List<ServiceResponse> res = null;
	try {
	    res = callBacks.getResults();
	} catch (InterruptedException e) {
	    logger.error("Error invoke sync web services", e);
	}

	// TODO depois que retornar o resultado, tavez seja interssante tratar
	// aqui os resultados quando exceptions acontecem

	stopWatch.stop("WSCAAExecutor.executeWSCAA.result");

	return res;
    }

}
